package com.newly.center.sys.service.role.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.newly.center.sys.service.auth.api.IAuthService;
import com.newly.center.sys.service.role.api.IRoleService;
import com.newly.center.sys.service.tenant.api.ITenantService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.entity.PageUtils;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.sys.auth.entity.dto.EntityDto;
import com.newly.common.base.entity.sys.auth.role.dto.RoleDto;
import com.newly.common.base.entity.sys.auth.role.po.RolePo;
import com.newly.common.base.entity.sys.auth.role.po.TenantRolePo;
import com.newly.common.base.entity.sys.tenant.bo.SimpleTenantBo;
import com.newly.common.core.utils.RequestUtils;
import com.newly.transfer.redis.RedisTransfer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RoleService extends AbstractRoleService implements IRoleService {

    @Autowired
    private RequestUtils requestUtils;

    @Autowired
    private IAuthService authService;

    @Autowired
    private ITenantService tenantService;

    @Autowired
    private RedisTransfer redisTransfer;

    @Transactional
    @Override
    public Integer save(RoleDto roleDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        Integer selfRoleId = currentTenant.getSelfRoleId();

        roleDto.setCreateBy(currentTenant.getUserId());
        roleDto.setParentId(selfRoleId);
        Integer roleId = this.insert(roleDto);
        Assert.notNull(roleId, "Failed to create role!");

        // 携带了权限
        List<EntityDto> permEntities = roleDto.getPermEntities();
        if (!ObjectUtil.isEmpty(permEntities)) {
            AuthConstant.AuthEntityType[] values = AuthConstant.AuthEntityType.values();
            for (AuthConstant.AuthEntityType value : values) {
                for (EntityDto permEntity : permEntities) {
                    if (permEntity.getTypeCode().equals(value.getCode())) {
                        boolean result = authEntityService.bind(value, roleId, permEntity.getEntityIds());
                        Assert.isTrue(result, "Role binding permissions failed!");
                    }
                }
            }
        }
        authService.flushAuthPayloads();
        return roleId;
    }

    @Transactional
    @Override
    public boolean modifyById(RoleDto roleDto) {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        // 不允许修改自身角色,并且不能够修改不属于自己的角色
        currentTenant.assertExcludeSelfRole(roleDto.getId())
                .assertIncludeRole(roleDto.getId());

        // 有权限绑定
        List<EntityDto> permEntities = roleDto.getPermEntities();
        if (!ObjectUtil.isEmpty(permEntities)) {
            Integer roleId = roleDto.getId();
            AuthConstant.AuthEntityType[] values = AuthConstant.AuthEntityType.values();
            for (AuthConstant.AuthEntityType value : values) {
                for (EntityDto permEntity : permEntities) {
                    if (permEntity.getTypeCode().equals(value.getCode())) {
                        // 先解绑权限实体
                        boolean result = authEntityService.unBind(roleId, value);
                        Assert.isTrue(result, "Role unbinding permissions failed!");
                        result = authEntityService.bind(value, roleId, permEntity.getEntityIds());
                        Assert.isTrue(result, "Role binding permissions failed!");
                    }
                }
            }
        }
        this.updateById(roleDto);

        return authService.flushAuthPayloads();
    }

    @Override
    public boolean removeByIds(List<Integer> roleIds) {
        // 当前租户不能够删除自己自身的角色和不属于它的子角色
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        if (!currentTenant.isDev()) {
            currentTenant.assertExcludeSelfRoles(roleIds)
                    .assertIncludeRoles(roleIds);
        }

        // 将被删除角色的子角色绑定到上级角色或当前角色下
        List<RolePo> delRolePos = this.getByIds(roleIds);
        for (RolePo delRolePo : delRolePos) {
            List<Integer> childrenIds = this.parseIds(getChildrenL1(delRolePo.getId()));
            if (ObjectUtil.isEmpty(childrenIds)) continue;

            Integer parentId = null;
            if (ObjectUtil.isEmpty(delRolePo.getParentId())) {
                parentId = currentTenant.getSelfRoleId();
            } else {
                parentId = delRolePo.getParentId();
            }

            boolean result = modifyParent(parentId, childrenIds);
            Assert.isTrue(result, "Failed to parent of modify role!");
        }
        this.deleteByIds(roleIds);
        return authService.flushAuthPayloads();
    }

    @Override
    public boolean removeById(Integer roleId) {
        return removeByIds(Arrays.asList(roleId));
    }

    @Override
    public boolean modifyParent(Integer parentId, List<Integer> childrenIds) {
        if (ObjectUtil.isEmpty(childrenIds) || ObjectUtil.isEmpty(parentId))
            return false;
        int count = 0;
        for (Integer childrenId : childrenIds) {
            RolePo rolePo = new RolePo();
            rolePo.setId(childrenId);
            rolePo.setParentId(parentId);
            count += this.updateById(rolePo);
        }
        return count > 0;
    }

    @Override
    public List<RolePo> getByTenantId(Long tenantId) {
        return selectByTenantIds(Arrays.asList(tenantId));
    }

    @Override
    public PageUtils list(Integer offset, Integer limit, String createBy, String fromTime, String toTime, String keyword) {
        LambdaQueryWrapper<RolePo> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(RolePo::getCreateTime);

        // 如果不是开发者获取当前租户角色
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        if (!currentTenant.isDev()) {
            List<Integer> roleIds = currentTenant.getRoleIds();
            if (ObjectUtil.isEmpty(roleIds)) return null;

            // 移除掉自己
            if (!ObjectUtil.isEmpty(currentTenant.getSelfRoleIds())) {
                roleIds.removeAll(currentTenant.getSelfRoleIds());
            }
            wrapper.in(RolePo::getId, roleIds);
        }

        // 创建人
        if (!ObjectUtil.isEmpty(createBy)) {
            wrapper.eq(RolePo::getCreateBy, createBy);
        }
        // 创建时间
        if (!StrUtil.isBlank(fromTime) && !StrUtil.isBlank(toTime)) {
            wrapper.between(RolePo::getCreateTime, fromTime, toTime);
        }
        // 关键字
        if (!StrUtil.isBlank(keyword)) {
            wrapper.like(RolePo::getName, keyword);
        }
        IPage<RolePo> iPage = this.selectPage(offset, limit, wrapper);
        if (!ObjectUtil.isEmpty(iPage) && !ObjectUtil.isEmpty(iPage.getRecords())) {
            List<RolePo> records = iPage.getRecords();
            for (RolePo record : records) {
                this.setProperties(record);
            }
        }
        return new PageUtils(iPage);
    }

    @Override
    public List<SimpleTenantBo> getCreateTenant() {
        List<RolePo> rolePos = this.getInAuth();
        if (ObjectUtil.isEmpty(rolePos)) return null;

        List<SimpleTenantBo> simpleTenantBos = new ArrayList<>();
        List<Long> tenantIds = this.parseTenantIds(rolePos);
        for (Long tenantId : tenantIds) {
            simpleTenantBos.add(tenantService.getSimpleTenant(tenantId));
        }
        return simpleTenantBos;
    }

    @Override
    public List<RolePo> getInAuth() {
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        List<Integer> roleIds = currentTenant.getRoleIds();

        // 如果不是开发者需要移除掉自己的角色
        if (!currentTenant.isDev()) {
            roleIds.removeAll(currentTenant.getSelfRoleIds());
        }
        return this.getByIds(roleIds);
    }

    @Override
    public List<RolePo> getAll() {
        return roleMapper.selectList(null);
    }

    @Override
    public List<RolePo> getByIds(List<Integer> roleIds) {
        List<RolePo> rolePos = selectByIds(roleIds);
        return rolePos;
    }

    @Override
    public RolePo getById(Integer roleId) {
        List<RolePo> rolePos = getByIds(Arrays.asList(roleId));
        if (!ObjectUtil.isEmpty(rolePos)) {
            return rolePos.get(0);
        }
        return null;
    }

    @Override
    public List<RolePo> getByParentIds(List<Integer> parentRoleIds) {
        return this.selectByParentIds(parentRoleIds);
    }

    @Override
    public List<RolePo> getByParentId(Integer parentRoleId) {
        return getByParentIds(Arrays.asList(parentRoleId));
    }

    @Override
    public void getChildren(List<Integer> parentRoleIds, List<RolePo> packageRoles) {
        if (ObjectUtil.isEmpty(parentRoleIds)) return;

        for (Integer parentRoleId : parentRoleIds) {
            List<RolePo> rolePos = this.getByParentId(parentRoleId);
            if (ObjectUtil.isEmpty(rolePos)) continue;

            packageRoles.addAll(rolePos);
            getChildren(parseIds(rolePos), packageRoles);
        }
    }

    @Override
    public List<RolePo> getChildrenL1(List<Integer> parentRoleIds) {
        return getByParentIds(parentRoleIds);
    }

    @Override
    public List<RolePo> getChildrenL1(Integer parentRoleId) {
        return getChildrenL1(Arrays.asList(parentRoleId));
    }

    @Override
    public List<SimpleTenantBo> getBindUserOfRole(String roleId) {
        if (StrUtil.isBlank(roleId)) return null;

        List<TenantRolePo> tenantRolePos = tenantRoleMapper.selectList(new LambdaQueryWrapper<TenantRolePo>()
                .eq(TenantRolePo::getRoleId, roleId));
        if (ObjectUtil.isEmpty(tenantRolePos)) return null;

        List<Long> tenantIds = tenantRolePos.stream().map(TenantRolePo::getUserId)
                .collect(Collectors.toList());
        List<SimpleTenantBo> simpleTenantBos = new ArrayList<>();
        for (Long tenantId : tenantIds) {
            SimpleTenantBo simpleTenant = tenantService.getSimpleTenant(tenantId);
            if (!ObjectUtil.isEmpty(simpleTenant)) {
                simpleTenantBos.add(simpleTenant);
            }
        }
        return simpleTenantBos;
    }

    @Override
    public boolean tenantBindRole(String tenantId, String roleId) {
        if (StrUtil.isBlank(tenantId) || StrUtil.isBlank(roleId))
            return false;

        // 不是超级管理员不允许绑定当前租户的自身角色并且当前租户拥有roleId该角色
        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
        if (!currentTenant.isDev()) {
            currentTenant.assertExcludeSelfRole(Integer.parseInt(roleId))
                    .assertIncludeRole(Integer.parseInt(roleId));
            // 不能控制不属性当前租户的子用户
            //currentTenant.assertIncludeEntity(AuthConstant.AuthEntityType.TENANT, tenantId);
        }

        // 如果当前租户已经绑定有角色则换绑
        TenantRolePo tenantRolePo = tenantRoleMapper.selectOne(new LambdaQueryWrapper<TenantRolePo>()
                .eq(TenantRolePo::getUserId, tenantId));
        int result = 0;
        if (!ObjectUtil.isEmpty(tenantRolePo)) {
            // 换绑
            tenantRolePo.setRoleId(Integer.parseInt(roleId));
            result = tenantRoleMapper.updateById(tenantRolePo);
        } else {
            // 绑定
            tenantRolePo = new TenantRolePo();
            tenantRolePo.setUserId(Long.parseLong(tenantId));
            tenantRolePo.setRoleId(Integer.parseInt(roleId));
            tenantRolePo.setCreateTime(DateUtil.date());
            result = tenantRoleMapper.insert(tenantRolePo);
        }
        return result > 0;
    }

}
